<template>
  <div class="chunk-file-uploader-file" :status="status">
    <slot
      :file="file"
      :list="list"
      :status="status"
      :paused="paused"
      :error="error"
      :response="response"
      :average-speed="averageSpeed"
      :formated-average-speed="formatAverageSpeed"
      :current-speed="currentSpeed"
      :is-complete="isComplete"
      :is-uploading="isUploading"
      :size="size"
      :formated-size="formatedSize"
      :uploaded-size="uploadedSize"
      :progress="progress"
      :progress-style="progressStyle"
      :progressing-class="progressingClass"
      :time-remaining="timeRemaining"
      :formated-time-remaining="formatedTimeRemaining"
      :type="type"
      :extension="extension"
      :file-category="fileCategory">
      <div class="chunk-file-uploader-file-info">
        <div class="chunk-file-uploader-file-name" :title="file.name">
          <i class="chunk-file-uploader-file-icon" :icon="fileCategory"></i>{{ file.name }}
        </div>
        <div class="chunk-file-uploader-file-size">{{ formatedSize }}</div>
        <div class="chunk-file-uploader-file-status">
          <span v-show="!['uploading', 'hashing'].includes(status)">{{ statusText }}</span>
          <span v-show="status === 'hashing'">{{ file.hashProgress }}</span>
          <span v-show="status === 'uploading'" style="padding-left: 4px">
            <span>{{ progressStyle.progress }}</span> | <em>{{ formatAverageSpeed }}</em> |
            <i>{{ formatedTimeRemaining }}</i></span
          >
        </div>
        <div class="chunk-file-uploader-file-actions">
          <span class="chunk-file-uploader-file-pause" @click="pause"></span>
          <span class="chunk-file-uploader-file-resume" @click="resume">️</span>
          <span class="chunk-file-uploader-file-retry" @click="retry"></span>
          <span class="chunk-file-uploader-file-remove" @click="remove"></span>
        </div>
      </div>
    </slot>
  </div>
</template>

<script>
  import Uploader from 'simple-uploader.js'
  import events from '../common/file-events'
  import { secondsToStr } from '../common/utils'

  export default {
    name: 'ChunkFileUploaderFile',
    props: {
      file: {
        type: Object,
        default() {
          return {}
        }
      },
      list: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        response: null,
        paused: false,
        error: false,
        averageSpeed: 0,
        currentSpeed: 0,
        isComplete: false,
        isUploading: false,
        size: 0,
        formatedSize: '',
        uploadedSize: 0,
        progress: 0,
        timeRemaining: 0,
        type: '',
        extension: '',
        progressingClass: ''
      }
    },
    computed: {
      fileCategory() {
        const extension = this.extension
        const isFolder = this.file.isFolder
        let type = isFolder ? 'folder' : 'unknown'
        const categoryMap = this.file.uploader.opts.categoryMap
        const typeMap = categoryMap || {
          image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
          video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
          audio: ['mp3', 'wav', 'wma', 'ogg', 'aac', 'flac'],
          document: [
            'doc',
            'txt',
            'docx',
            'pages',
            'epub',
            'pdf',
            'numbers',
            'csv',
            'xls',
            'xlsx',
            'keynote',
            'ppt',
            'pptx'
          ]
        }
        Object.keys(typeMap).forEach(_type => {
          const extensions = typeMap[_type]
          if (extensions.indexOf(extension) > -1) {
            type = _type
          }
        })
        return type
      },
      progressStyle() {
        const progress = Math.floor(this.progress * 100)
        const style = `translateX(${Math.floor(progress - 100)}%)`
        return {
          progress: `${progress}%`,
          webkitTransform: style,
          mozTransform: style,
          msTransform: style,
          transform: style
        }
      },
      formatAverageSpeed() {
        return `${Uploader.utils.formatSize(this.averageSpeed)} / s`
      },
      status() {
        const { isUploading, isComplete, isError, paused } = this
        const { hashing } = this.file
        if (hashing) {
          return 'hashing'
        } else if (isComplete) {
          return 'success'
        } else if (isError) {
          return 'error'
        } else if (isUploading) {
          return 'uploading'
        } else if (paused) {
          return 'paused'
        } else {
          return 'waiting'
        }
      },
      statusText() {
        const status = this.status
        const fileStatusText = this.file.uploader.fileStatusText
        let txt = status
        if (typeof fileStatusText === 'function') {
          txt = fileStatusText(status, this.response)
        } else {
          txt = fileStatusText[status]
        }
        return txt || ''
      },
      formatedTimeRemaining() {
        const timeRemaining = this.timeRemaining
        const file = this.file
        if (timeRemaining === Number.POSITIVE_INFINITY || timeRemaining === 0) {
          return ''
        }
        let parsedTimeRemaining = secondsToStr(timeRemaining)
        const parseTimeRemaining = file.uploader.opts.parseTimeRemaining
        if (parseTimeRemaining) {
          parsedTimeRemaining = parseTimeRemaining(timeRemaining, parsedTimeRemaining)
        }
        return parsedTimeRemaining
      }
    },
    watch: {
      status(newStatus, oldStatus) {
        if (oldStatus && newStatus === 'uploading' && oldStatus !== 'uploading') {
          this.tid = setTimeout(() => {
            this.progressingClass = 'uploader-file-progressing'
          }, 200)
        } else {
          clearTimeout(this.tid)
          this.progressingClass = ''
        }
      }
    },
    mounted() {
      const staticProps = ['paused', 'error', 'averageSpeed', 'currentSpeed']
      const fnProps = [
        'isComplete',
        'isUploading',
        {
          key: 'size',
          fn: 'getSize'
        },
        {
          key: 'formatedSize',
          fn: 'getFormatSize'
        },
        {
          key: 'uploadedSize',
          fn: 'sizeUploaded'
        },
        'progress',
        'timeRemaining',
        {
          key: 'type',
          fn: 'getType'
        },
        {
          key: 'extension',
          fn: 'getExtension'
        }
      ]
      staticProps.forEach(prop => {
        this[prop] = this.file[prop]
      })
      fnProps.forEach(fnProp => {
        if (typeof fnProp === 'string') {
          this[fnProp] = this.file[fnProp]()
        } else {
          this[fnProp.key] = this.file[fnProp.fn]()
        }
      })

      const handlers = (this._handlers = {})
      const eventHandler = event => {
        handlers[event] = (...args) => {
          this.fileEventsHandler(event, args)
        }
        return handlers[event]
      }
      events.forEach(event => {
        this.file.uploader.on(event, eventHandler(event))
      })
    },
    destroyed() {
      events.forEach(event => {
        this.file.uploader.off(event, this._handlers[event])
      })
      this._handlers = null
    },
    methods: {
      _actionCheck() {
        this.paused = this.file.paused
        this.error = this.file.error
        this.isUploading = this.file.isUploading()
      },
      pause() {
        this.file.pause()
        this._actionCheck()
        this._fileProgress()
      },
      resume() {
        this.file.resume()
        this._actionCheck()
      },
      remove() {
        this.$set(this.file, 'canceled', true)
        this.file.cancel()
      },
      retry() {
        this.file.retry()
        this._actionCheck()
      },
      processResponse(message) {
        let res = message
        try {
          res = JSON.parse(message)
        } catch (e) {}
        this.response = res
      },
      fileEventsHandler(event, args) {
        const rootFile = args[0]
        const file = args[1]
        const target = this.list ? rootFile : file
        if (this.file === target) {
          if (this.list && event === 'fileSuccess') {
            this.processResponse(args[2])
            return
          }
          this[`_${event}`].apply(this, args)
        }
      },
      _fileProgress() {
        this.progress = this.file.progress()
        this.averageSpeed = this.file.averageSpeed
        this.currentSpeed = this.file.currentSpeed
        this.timeRemaining = this.file.timeRemaining()
        this.uploadedSize = this.file.sizeUploaded()
        this._actionCheck()
      },
      _fileSuccess(rootFile, file, message) {
        if (rootFile) {
          this.processResponse(message)
        }
        this._fileProgress()
        this.error = false
        this.isComplete = true
        this.isUploading = false
      },
      _fileComplete() {
        this._fileSuccess()
      },
      _fileError(rootFile, file, message) {
        this._fileProgress()
        this.processResponse(message)
        this.error = true
        this.isComplete = false
        this.isUploading = false
      }
    }
  }
</script>

<style lang="scss">
  .chunk-file-uploader-file {
    position: relative;
    height: 49px;
    overflow: hidden;
    line-height: 49px;
    border-bottom: 1px solid #cdcdcd;
  }

  .chunk-file-uploader-file[status='waiting'] .chunk-file-uploader-file-pause,
  .chunk-file-uploader-file[status='uploading'] .chunk-file-uploader-file-pause {
    display: block;
  }

  .chunk-file-uploader-file[status='paused'] .chunk-file-uploader-file-resume {
    display: block;
  }

  .chunk-file-uploader-file[status='error'] .chunk-file-uploader-file-retry {
    display: block;
  }

  .chunk-file-uploader-file[status='success'] .chunk-file-uploader-file-remove {
    display: none;
  }

  .chunk-file-uploader-file[status='error'] .chunk-file-uploader-file-progress {
    background: #ffe0e0;
  }

  .chunk-file-uploader-file-progress {
    position: absolute;
    width: 100%;
    height: 100%;
    background: #e2eeff;
    transform: translateX(-100%);
  }

  .chunk-file-uploader-file-progressing {
    transition: all 0.4s linear;
  }

  .chunk-file-uploader-file-info {
    position: relative;
    z-index: 1;
    height: 100%;
    overflow: hidden;

    @include flex-row;
  }

  .chunk-file-uploader-file-info:hover {
    background-color: rgba(240, 240, 240, 0.2);
  }

  .chunk-file-uploader-file-info i,
  .chunk-file-uploader-file-info em {
    font-style: normal;
  }

  .chunk-file-uploader-file-name,
  .chunk-file-uploader-file-size,
  .chunk-file-uploader-file-meta,
  .chunk-file-uploader-file-status,
  .chunk-file-uploader-file-actions {
    position: relative;
    height: 100%;
    padding: 0 10px;
  }

  .chunk-file-uploader-file-size,
  .chunk-file-uploader-file-meta,
  .chunk-file-uploader-file-status,
  .chunk-file-uploader-file-actions {
    @include flex-not-shrink;
  }

  .chunk-file-uploader-file-name {
    overflow: hidden;
    white-space: nowrap;
    text-indent: 14px;
    text-overflow: ellipsis;

    @include flex-item;
  }

  .chunk-file-uploader-file-icon {
    display: inline-block;
    width: 24px;
    height: 24px;
    margin-top: 13px;
    margin-right: 8px;
    vertical-align: top;
  }

  .chunk-file-uploader-file-icon::before {
    display: block;
    height: 100%;
    color: #808080;
    font-size: 24px;
    line-height: 1;
    text-indent: 0;
    content: '📃';
  }

  .chunk-file-uploader-file-icon[icon='folder']::before {
    content: '📂';
  }

  .chunk-file-uploader-file-icon[icon='image']::before {
    content: '📊';
  }

  .chunk-file-uploader-file-icon[icon='video']::before {
    content: '📹';
  }

  .chunk-file-uploader-file-icon[icon='audio']::before {
    content: '🎵';
  }

  .chunk-file-uploader-file-icon[icon='document']::before {
    content: '📋';
  }

  .chunk-file-uploader-file-size {
    // text-indent: 10px;
  }

  .chunk-file-uploader-file-meta {
    // width: 8%;
  }

  .chunk-file-uploader-file-status {
    // text-indent: 20px;
  }

  .chunk-file-uploader-file-actions {
    // width: 10%;
  }

  .chunk-file-uploader-file-actions > span {
    display: none;
    float: left;
    width: 16px;
    height: 16px;
    margin-top: 16px;
    margin-right: 10px;
    background: url('')
      no-repeat 0 0;
    cursor: pointer;
  }

  .chunk-file-uploader-file-actions > span:hover {
    background-position-x: -21px;
  }

  .chunk-file-uploader-file-actions .chunk-file-uploader-file-pause {
    background-position-y: 0;
  }

  .chunk-file-uploader-file-actions .chunk-file-uploader-file-resume {
    background-position-y: -17px;
  }

  .chunk-file-uploader-file-actions .chunk-file-uploader-file-retry {
    background-position-y: -53px;
  }

  .chunk-file-uploader-file-actions .chunk-file-uploader-file-remove {
    display: block;
    background-position-y: -34px;
  }
</style>
